/*-------------------------------------------------------------------------
 *
 * rmtree.c
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/common/rmtree.c
 *
 *-------------------------------------------------------------------------
 */

#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

#include <unistd.h>
#include <sys/stat.h>

#ifndef FRONTEND
#define pg_log_warning(...) elog(WARNING, __VA_ARGS__)
#else
#include "common/logging.h"
#endif


/*
 *	rmtree
 *
 *	递归删除目录树。
 *	假设路径指向有效目录。
 *	删除路径下的所有内容。
 *	如果rmtopdir为真，则还删除目录。
 *	如果成功返回true，如果有任何问题则返回false。
 *	（问题的细节已报告，因此调用者并不需要再说什么，
 *	但大多数人会说。）
 */
bool rmtree(const char *fc_path, bool fc_rmtopdir)
{
	bool		fc_result = true;
	char		fc_pathbuf[MAXPGPATH];
	char	  **fc_filenames;
	char	  **fc_filename;
	struct stat fc_statbuf;

	/*
	 * 在开始修改之前，我们复制目录中的所有名称。
	 */
	fc_filenames = pgfnames(fc_path);

	if (fc_filenames == NULL)
		return false;

	/* 现在我们有了名称，可以开始删除东西 */
	for (fc_filename = fc_filenames; *fc_filename; fc_filename++)
	{
		snprintf(fc_pathbuf, MAXPGPATH, "%s/%s", fc_path, *fc_filename);

		/*
		 * 如果文件不再存在也没关系；反正我们正打算
		 * 删除它。
		 *
		 * 这不是一个学术上的可能性。发生这种情况的一个场景
		 * 是当 bgwriter 有一个待解除链接的请求，针对的是
		 * 一个正在被删除的数据库。在 dropdb() 中，我们调用
		 * ForgetDatabaseSyncRequests() 来刷新任何此类待解除链接
		 * 的请求，但因为这是异步的，所以不能保证 bgwriter
		 * 能及时收到消息。
		 */
		if (lstat(fc_pathbuf, &fc_statbuf) != 0)
		{
			if (errno != ENOENT)
			{
				pg_log_warning("could not stat file or directory \"%s\": %m",
							   fc_pathbuf);
				fc_result = false;
			}
			continue;
		}

		if (S_ISDIR(fc_statbuf.st_mode))
		{
			/* 递归调用自己以处理目录 */
			if (!rmtree(fc_pathbuf, true))
			{
				/* 我们已经报告了错误 */
				fc_result = false;
			}
		}
		else
		{
			if (unlink(fc_pathbuf) != 0)
			{
				if (errno != ENOENT)
				{
					pg_log_warning("could not remove file or directory \"%s\": %m",
								   fc_pathbuf);
					fc_result = false;
				}
			}
		}
	}

	if (fc_rmtopdir)
	{
		if (rmdir(fc_path) != 0)
		{
			pg_log_warning("could not remove file or directory \"%s\": %m",
						   fc_path);
			fc_result = false;
		}
	}

	pgfnames_cleanup(fc_filenames);

	return fc_result;
}
